package com.gitee.wsl.jvm.net.security.token

import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import okhttp3.Request
import java.time.Instant


/**
 * A hypothetical token manager that stores an auth token. Just in-memory and not thread-safe for
 * now.
 */
class TokenManager(private val refreshToken: (request: Request)->Result<AuthenticationResponse>, private val tokenStorage: TokenStorage) {
    private val mutex = Mutex()

    /** Authenticates a [request]. */
    suspend fun authenticate(request: Request): Request {
        return authenticate(request, false)
    }

    private suspend fun authenticate(request: Request, isAfterRefresh: Boolean): Request {
        println("INFO: Authenticating request ${request.url}")
        val newBuilder = request.newBuilder()
        val (tokenType, expiration, token) =
            tokenStorage.getAuthData()
                ?: run {
                    refreshToken(request)
                    return authenticate(request, isAfterRefresh)
                }
        if (Instant.now().isAfter(expiration)) {
            check(!isAfterRefresh)
            refreshToken(request)
            return authenticate(request, isAfterRefresh)
        } else {
            newBuilder.addHeader("Authorization", "$tokenType $token")
        }

        return newBuilder.build()
    }

    private suspend fun refreshToken(request: Request) =
        mutex.withLock {
            println("INFO: Refreshing token")
            refreshToken.invoke(request).onSuccess {
                tokenStorage.updateAuthData(it)
            }.onFailure {
                println("ERROR: Failed to refresh token: $it")
            }
        }
}
